package day09;

// 父子类中成员的特点
public class Demo03 {
	public static void main(String[] args) {
		Sub s = new Sub();
		//		System.out.println(s.num01);
		//		System.out.println(s.num03);
		//		System.out.println(s.num02); // 4
		s.show();
	}
}

// -----------------------

class Parent {
	static {
		System.out.println("父类中的静态代码块");
	}

	public Parent() {
		System.out.println("父类的无参构造");
	}

	int num01 = 1;
	int num02 = 2;

	public void show() {
		System.out.println("Parent.show()");
	}
}

class Sub extends Parent {
	int num03 = 3;
	int num02 = 4;

	static {
		System.out.println("子类中的静态代码块");
	}

	public Sub() {
		// super();表示调用父类构造方法
		// 即使子类构造方法中不写这条语句
		// 它也是隐式自动存在的
		super();
		System.out.println("子类的无参构造");
	}

	// 子类中有和父类中完全一样的方法
	// 当子类对象调用方法时, 执行的是子类的方法
	// 这种情况称为: 方法的重写
	// 构成方法重写的要素
	// 1. 发生在父子类中
	// 2. 子类方法与父类方法的方法名/返回值类型/参数列表都一样
	// 3. 父子类中方法的具体实现可以不一样
	// 4. 构造方法不能重写
	// 何时用重写: 当子类继承父类, 沿袭了父类的功能
	// 但子类对该功能的具体实现与父类不同
	// 此时没必要在子类中重新定义方法, 可以直接对父类的方法进行重写
	// Java提供了一种手段检查某个方法是否是重写的方法: @Override注解
	// 当某方法标注了该注解, 那么该方法就必须符合重写的要素
	// 如果不是, 编译报错
	// 继承并重写方法的好处: 在不影响原有代码的基础上, 提高功能的扩展性
	@Override
	public void show() {
		System.out.println("Sub.show()");
		//		System.out.println(num01); // 1
		//		System.out.println(num02); // 4
		//		System.out.println(num03); // 3
		// 在子类中访问父类的成员
		//		System.out.println(super.num02); // 2
		// 当父子类中出现了非私有的同名成员变量时
		// 子类要访问本类的变量, 用this引用
		// 如果想要访问父类的变量, 用super引用
		// 如果在访问时, 没有加任何引用, 默认是this
	}

}

// 创建对象时, 完整的内存操作过程
// 1. 加载父类
// 2. 加载父类的静态成员, 并初始化
// 3. 执行父类的静态代码块
// 4. 加载子类
// 5. 加载子类的静态成员, 并初始化
// 6. 执行子类的静态代码块
// 7. 在堆中开辟父类的实例空间
// 8. 加载父类的实例变量, 并初始化
// 9. 执行父类的构造代码块
// 10. 执行父类的构造方法
// 11. 在堆中开辟子类的实例空间
// 12. 从父类的实例空间中, 将继承的实例变量加载到子类实例空间中
// 13. 加载子类自己的实例变量, 并初始化
// 14. 执行子类的构造代码块
// 15. 执行子类的构造方法
// 16. 将子类实例空间的内存地址赋值给子类对象的引用变量

// 实际开发中需要遵守2大基本原则
// 开闭原则
// 开: 对扩展开
// 闭: 对修改闭
// 高低原则